webpack 打包工具入門


Posted by Nicolakacha on 2020-09-09

為什麼需要 webpack?

瀏覽器不支援 JavaScript 的 modules,所以我們透過 webpack 把寫好的 JavaScript 模組打包成一包,變成成瀏覽器可以看得懂的樣子。

index.js, utils.js → 經過 webpack 打包 → main.js

使用方法

在專案資料夾內安裝 webpack:

mkdir webpack-demo // 建立專案資料夾
cd webpack-demo // 切換目錄到專案資料夾
npm init -y // npm 初始化,建立 package.json
npm install webpack webpack-cli --save-dev // 安裝 webpack

通常開發者會把原始的檔案放在 src 資料夾,編譯過後的檔案放在 dist 資料夾,以方便管理。

執行 webpack 就可以進行編譯,預設會把 src 裡的檔案打包到 dist 內成名為 main.js 的檔案:

webpack

也可以在根目錄建立設定檔 webpack.config.js 來自定義路徑和要打包的模式,模式有兩種:

// production 的壓縮程度較高,檔案會變比較小,類似 uglify 過後的一串 JavaScript
// development 可讀性較高

const path = require('path');

module.exports = {
  mode: 'production',
  entry: './src/index.js',
  output: {
    filename: 'main.js',
    path: path.resolve(__dirname, 'dist'),
  },
};

可以在 package.json 建立 script:

"scripts": {
    "build": "webpack",
    "test": "echo \"Error: no test specified\" && exit 1"
  },

建立完 script 之後,輸入 npm run build 就可以執行 webpack 了:

npm run build

打包 npm 上別人第三方的 modules

webpack 強大之處在於它也可以把我們引入的第三方 modules 也一起打包進來。

使用 Loader 打包各種資源

除了打包 JavaScript 之外,webpack 把 modules 的概念延伸至更廣的範圍,各種資源都可以視為一個 module,所以我們也可以利用 webpack 幫我們打包圖片或 CSS。透過各種 loader 把資源載入 webpack,也可以順便做到 uglify JavaScript 或是 minify CSS 之類的編譯,以下會介紹一些常見的 loader 的使用。

css-loader

以打包 CSS 為例,在 webpack.config.js 加入 loader:

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
};

安裝 style.loader 和 css.loader:

npm install --save-dev css-loader style-loader

準備好 css 檔案:

body {
  background: rgba(255, 0, 0, 0.3);
}

把 style.css 引入 index.js:

import css from './style.css';
import $ from 'jquery';
import { first } from './utils';

$(document).ready(()=>{
  $('.btn').click(()=> {
    alert(first('hello'));
  })
})

打包成 main.js:

npm run build

在 index.html 引入 main.js:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <script src="dist/main.js"></script>
  <title>Document</title>
</head>
<body>
  <button class="btn">click me</button>
</body>
</html>

這樣就可以把 style.css 也融入 main.js 一起讓瀏覽器讀到了,它的原理其實就只是透過 DOM 元素動態建立一個 區塊並把 .css 當成字串插入 區塊裡面:

其他 loader 的使用也很簡單,安裝完之後,在 webpack.config.js 設定 loader 就可以了!

babel-loader

安裝:

npm install -D babel-loader @babel/core @babel/preset-env

在 webpack.config.js 設定檔加入:

rules: [
  {
    test: /\.m?js$/,
    exclude: /(node_modules|bower_components)/,
    use: {
      loader: 'babel-loader',
      options: {
        presets: ['@babel/preset-env']
      }
    }
  }

sass-loader

安裝:

npm install sass-loader sass webpack --save-dev

在 webpack.config.js 設定檔加入:

rules: [
  {
    test: /\.s[ac]ss$/i,
    use: [
      // Creates `style` nodes from JS strings
      'style-loader',
      // Translates CSS into CommonJS
      'css-loader',
      // Compiles Sass to CSS
      'sass-loader',
    ],
  },
],

利用 Dev Server 自動化打包

dev server 是一套可以在檔案改變的時候自動重新打包的 plugin。

要使用 dev server,一樣先安裝起來:

npm install webpack-dev-server --save-dev

在設定檔中加入以下程式碼,告訴 dev server 最後編譯完的檔案放在哪裡:

devServer: {
     contentBase: './dist',
 },

在 package.json 加入 start 指令:

"scripts": {
     "start": "webpack-dev-server --open",
}

暫時把 html 檔案也放在 dist 資料夾內,執行 npm run start:

npm run start

就會開始進入 dev server start 的狀態,之後 src 裡面的任何檔案有更改過,都會被 dev server 偵測到並自動重新打包:

使用 source map 看到原來程式碼的樣子

在 webpack.config.js 加入 source-map 程式碼:

module.exports = {
    mode: 'development',
    entry: {
      app: './src/index.js',
    },
      devtool: 'inline-source-map',
    output: {
      filename: '[name].bundle.js',
      path: path.resolve(__dirname, 'dist'),
    },
  };

在 debug 的時候,就可以在 chrome dev tool 溯源到真正的程式碼:

使用 HtmlWebpackPlugin

利用這個套件可以自動產生 HTML 檔案給我們的 webpack bundle 使用

安裝 HtmlWebpackPlugin:

npm install --save-dev html-webpack-plugin

在 webpack.config.js 加入 HtmlWebpackPlugin:
const HtmlWebpackPlugin = require('html-webpack-plugin');

並加上 plugins: [new HtmlWebpackPlugin()]

const HtmlWebpackPlugin = require('html-webpack-plugin');
const path = require('path');

module.exports = {
  entry: 'index.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'index_bundle.js'
  },
  plugins: [new HtmlWebpackPlugin()]
};

自動產生的 HTML:

webpack 和 gulp 的差異

雖然這次介紹的 webpack 和上次介紹的 gulp 都有做到像是 babel, sass 轉 css 的功能,但它們本質上是不同的工具。

gulp 是一套 task manager,它的 task 可以有很多種,而不只侷限於 babel, sass, uglify js 這些,但它做不到像 webpack 那樣把所有的資源打包在一起。

而 webpack 的主要還是個打包工具,其目的是為了要讓瀏覽器能夠支援 module,只是我們在透過各種 loader 把資源載入給 webpack 打包成一包時,也可以進行 babel, sass 轉 css 之類的功能,所以才會覺得 gulp 和 webpack 很混淆。

簡單來說就是,gulp 可以做各種 tasks 但做不到打包,webpack 能做把各種資源打包,但做不到很多 gulp 才能做到的 tasks。

補充資源:

webpack 新手教學之淺談模組化與 snowpack


#Webpack #frontend







Related Posts

LeetCode JS Easy 2704. To Be Or Not To Be

LeetCode JS Easy 2704. To Be Or Not To Be

Alibaba Cloud Learning Path

Alibaba Cloud Learning Path

為什麼需要 React / 思考模式的差異 / state vs props

為什麼需要 React / 思考模式的差異 / state vs props


Comments